﻿/*  ----------------------------------------------------------------------------
 *  SysServer
 *  ----------------------------------------------------------------------------
 *  File:       EntityPool.cs
 *  Creater:     SEEN\MistSeen
 *  ----------------------------------------------------------------------------
 *  $Date: 2010-09-06 10:36:01 +0000 (Mon, 06 Sep 2010) $
 *  $Rev: 15 $
 *  $Author: ScorDeity $
 *  $URL: http://sysserver.googlecode.com/svn/trunk/src/SysServer/Collections/EntityPool.cs $
 *  $Id: EntityPool.cs 15 2010-09-06 10:36:01Z ScorDeity $
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SysServer.Collections
{
    public class EntityPool
    {
        private TEntityFactory _Factory;
        private bool _CreateNewEntity = false;
        private object _Head;
        private object _Tail;
        private long _Count;

        /// <summary>
        /// 在池子里面的 Entity总数
        /// </summary>
        public long Count
        {
            get { return _Count; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="createNewEntity"></param>
        public EntityPool(TEntityFactory factory, bool createNewEntity)
        {
            this._Factory = factory;
            this._CreateNewEntity = createNewEntity;
            this.Init(0);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="createNewEntiy"></param>
        /// <param name="entityCount"></param>
        public EntityPool(TEntityFactory factory, bool createNewEntiy, int entityCount)
        {
            this._Factory = factory;
            this._CreateNewEntity = createNewEntiy;
            this.Init(entityCount);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="entityCount"></param>
        private void Init(int entityCount)
        {
            this._Head = this._Tail = this._Factory.CreateTEntiy();

            if (entityCount > 0)
            {
                for (int i = 1; i < entityCount; i++)
                {
                    this.Add(this._Factory.CreateTEntiy());
                }
            }

        }
        /// <summary>
        /// Add the poolable object to the object pool. The object is uninitialized before adding it to the pool.
        /// </summary>
        /// <param name="newEntity"></param>
        public void Add(TEntity newEntity)
        {
            newEntity.UnInitialize();
            TEntity tempTail = null;
            TEntity tempTailNext = null;

            do
            {
                tempTail = this._Tail as TEntity;
                tempTailNext = tempTail.LinkedEntity as TEntity;

                if (tempTail == this._Tail)
                {
                    if (tempTailNext == null)
                    {
                        // If the tail node we are referring to is really the last
                        // node in the queue (i.e. its next node is null), then
                        // try to point its next node to our new node
                        //
                        if (Interlocked.CompareExchange(ref tempTail.LinkedEntity, newEntity, tempTailNext) == tempTailNext)
                        {
                            break;
                        }
                    }
                    else
                    {
                        // This condition occurs when we have failed to update
                        // the tail's next node. And the next time we try to update
                        // the next node, the next node is pointing to a new node
                        // updated by other thread. But the other thread has not yet
                        // re-pointed the tail to its new node.
                        // So we try to re-point to the tail node to the next node of the
                        // current tail
                        //
                        Interlocked.CompareExchange(ref this._Tail, tempTailNext, tempTail);
                    }
                }
            } while (true);
            // If we were able to successfully change the next node of the current tail node
            // to point to our new node, then re-point the tail node also to our new node
            //
            Interlocked.CompareExchange(ref this._Tail, newEntity, tempTail);
            Interlocked.Increment(ref this._Count);
        }
        /// <summary>
        /// Returns an existing object from the pool or creates a 
        /// new object if the pool is empty. If an existing object is being
        /// returned it is initialized before returned to the caller.
        /// </summary>
        /// <returns>PoolableObject instance</returns>
        public TEntity GetEntity()
        {
            bool empty = false;
            TEntity tempTail = null;
            TEntity tempHead = null;
            TEntity tempHeadNext = null;
            do
            {
                tempHead = this._Head as TEntity;
                tempHeadNext = tempHead.LinkedEntity as TEntity;
                tempTail = this._Tail as TEntity;

                if (tempHead == this._Head)
                {
                    if (tempHeadNext == null)
                    {
                        empty = true;
                        break;
                    }
                    else
                    {
                        // Some other thread could be in the middle of the
                        // enqueue operation. it could have changed the next node of the tail
                        // to point to the new node.
                        // So let us advance the tail node to point to the next node of the
                        // current tail
                        Interlocked.CompareExchange(ref this._Tail, tempHeadNext, tempTail);
                    }
                }
                else
                {
                    // Move head one element down. 
                    // If succeeded Try to get the data from head and
                    // break out of the loop.
                    if (Interlocked.CompareExchange(ref this._Head, tempHeadNext, tempHead) == tempHead)
                    {
                        break;
                    }
                }                
            } while (true);

            if (empty == false)
            {
                Interlocked.Decrement(ref this._Count);
                tempHead.Initialize();
            }
            else
            {
                if (this._CreateNewEntity == true)
                {
                    tempHead = this._Factory.CreateTEntiy();
                }
            }
            return tempHead;
        }
        /// <summary>
        /// Removes all the poolable objects from the pool.
        /// </summary>
        public void Clear()
        {
            this.Clear(0);
        }
        /// <summary>
        /// Removes all the poolable objects from the pool. And fills the pool 
        /// with n-number of pre-created objects
        /// </summary>
        public void Clear(int entityCount)
        {
            Interlocked.Exchange(ref this._Count, 0);
            this.Init(entityCount);
        }
    }
}